package org.jackysoft.query;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jackysoft.data.QueryFieldTypeAliasRegistry;
import org.jackysoft.entity.nosql.AbstractNoSqlEntity;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.query.Query;
import org.springframework.util.CollectionUtils;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
 * <dd>通用查询类 <dd>代表一次查询收集上来的条件
 **/
public class RunnerQuery implements Serializable,Cloneable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4194262898401166379L;

	private static final Log logger = LogFactory.getLog(RunnerQuery.class);

	protected static final QueryFieldTypeAliasRegistry TYPES = new QueryFieldTypeAliasRegistry();
	
	
	protected String prefix = "a.";

	protected List<QueryField> fields = Lists.newArrayList();

	protected List<Order> orders = Lists.newArrayList();

	protected GroupBy groupBy = new GroupBy();

	protected String sqlId;

	protected String express;

	protected boolean useCache = true;

	protected String resultCode;

	protected int page;

	protected int rows;

	/**
	 * 通用前缀
	 **/
	public String getPrefix() {
		return prefix;
	}

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	public List<QueryField> getFields() {
		return fields;
	}

	public void setFields(List<QueryField> fields) {
		this.fields = fields;
	}

	public RunnerQuery addField(String name,String value){
		this.fields.add(new QueryField(name,value));
		
		return this;
	}
	
	public RunnerQuery addField(String name,CompareRelation inOpr,String value){
		this.fields.add(new QueryField(name,inOpr,value));
		return this;
	}
	
	public RunnerQuery addOrder(String name,String style){
		this.orders.add(new Order(name,style));
		return this;
	}
	
	public List<Order> getOrders() {
		return orders;
	}

	public void setOrders(List<Order> orders) {
		this.orders = orders;
	}


	public String getSqlId() {
		return sqlId;
	}

	public void setSqlId(String sqlId) {
		this.sqlId = sqlId;
	}

	public GroupBy getGroupBy() {
		return groupBy;
	}

	public void setGroupBy(GroupBy groupBy) {
		this.groupBy = groupBy;
	}

	public String getExpress() {
		return express;
	}

	public void setExpress(String express) {
		this.express = express;
	}

	public boolean isUseCache() {
		return useCache;
	}

	public void setUseCache(boolean useCache) {
		this.useCache = useCache;
	}

	public String getResultCode() {
		return resultCode;
	}

	public void setResultCode(String resultCode) {
		this.resultCode = resultCode;
	}

	public String groupBy() {

		return this.groupBy.groupString();
	}

	/**
	 * 组合的最终SQL查询字符串
	 **/
	public String queryString() {

		if (CollectionUtils.isEmpty(fields) && CollectionUtils.isEmpty(orders))
			return " 1=1 ";

		String qs = "";
		org.apache.commons.collections.CollectionUtils.filter(fields,
				new EmptyQueryFieldPredicate());
		ListIterator<QueryField> litr = fields.listIterator();

		int size = fields.size();
		for (; litr.hasNext();) {
			QueryField qf = litr.next();
			if (qf == null)
				continue;
			if (StringUtils.isEmpty(qf.getPrefix()))
				qf.setPrefix(this.getPrefix());
			int nidx = litr.nextIndex();
			if (nidx == size) {
				qf.setNoOutOpr(true);
			}
			qs += qf;
		}
		if (StringUtils.isBlank(qs))
			qs = " 1=1 ";

		ListIterator<Order> oitr = orders.listIterator();
		if (orders.size() > 0) {
			qs += " order by ";
			Collection<String> orderstr = Lists.newArrayList();
			for (; oitr.hasNext();) {
				Order odr = oitr.next();
				if (odr == null)
					continue;
				orderstr.add(odr.getName() + " " + odr.getStyle());
			}
			if (orderstr.size() > 0) {
				qs += StringUtils.join(orderstr, ',');
			}

		}
		String qstring = StringUtils.isEmpty(qs) ? " 1=1 " : qs;
		logger.debug("查询字符串:" + qstring);

		return qstring;
	}

	public String getSqlQuery(){
		return " where "+queryString();
	}
	
	/**
	 * 组合的最终SQL查询字符串
	 **/
	public String queryString(String pf) {

		if (CollectionUtils.isEmpty(fields) && CollectionUtils.isEmpty(orders))
			return " 1=1 ";

		String qs = "";
		org.apache.commons.collections.CollectionUtils.filter(fields,
				new EmptyQueryFieldPredicate());
		ListIterator<QueryField> litr = fields.listIterator();

		int size = fields.size();
		for (; litr.hasNext();) {
			QueryField qf = litr.next();
			if (qf == null)
				continue;
			qf.setPrefix(pf);
			int nidx = litr.nextIndex();
			if (nidx == size) {
				qf.setNoOutOpr(true);
			}
			qs += qf;
		}
		if (StringUtils.isBlank(qs))
			qs = " 1=1 ";
		ListIterator<Order> oitr = orders.listIterator();
		if (orders.size() > 0) {
			qs += " order by ";
			Collection<String> orderstr = Lists.newArrayList();
			for (; oitr.hasNext();) {
				Order odr = oitr.next();
				if (odr == null)
					continue;
				orderstr.add(odr.orderString(pf));
			}
			if (orderstr.size() > 0) {
				qs += StringUtils.join(orderstr, ',');
			}

		}

		return StringUtils.isEmpty(qs) ? " 1=1 " : qs;
	}

	public String semanticHttpQuery(){
		if (CollectionUtils.isEmpty(fields))return "";

		Set<String> qset = Sets.newHashSet();
		org.apache.commons.collections.CollectionUtils.filter(fields,
				new EmptyQueryFieldPredicate());
		ListIterator<QueryField> litr = fields.listIterator();
		for (; litr.hasNext();) {
			QueryField qf = litr.next();
			if (qf == null)
				continue;					
			qf.setNoOutOpr(true);
		    qset.add(qf.toSemanticString());
		}
		
		Joiner jn = Joiner.on('&');		
		String qs = jn.join(qset);
		
		return qs;
	}
	
	
	/**
	 * 返回mongodb格式的查询条件 
	 * TODO 根据传入的查询条件组装查询形式
	 * */

	public <T extends AbstractNoSqlEntity> Query<T> mongoQuery(final Datastore ds,Class<T>  type) {
		
		Query<T> query = ds.createQuery(type);
		if(ds==null) throw new java.lang.ArithmeticException("argument of mongoQuery can not be null");
		
		for (QueryField qf : this.getFields()) {
		    Class<?>  ctype = TYPES.resolveAlias(qf.getType());
		    Constructor<?> c;
		    Object oval = null;
			try {
				c      = ctype.getConstructor(String.class);
				oval   =  c.newInstance(qf.getValue());
			} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				logger.error(e);				
			}		  
		    query.filter(qf.getName(), oval);
			
		}

		return query;
	}

	protected String httpQuery;

	public String getHttpQuery() {
		if (CollectionUtils.isEmpty(fields))
			return "";

		httpQuery = "";
		org.apache.commons.collections.CollectionUtils.filter(fields,
				new EmptyQueryFieldPredicate());
		int i = 0;
		for (QueryField qf : fields) {
			httpQuery += String.format("query.fields[%d].inOpr=%s&", i,
					qf.getInOpr());
			httpQuery += String.format("query.fields[%d].name=%s&", i,
					qf.getName());
			httpQuery += String.format("query.fields[%d].outOpr=%s&", i,
					qf.getOutOpr());
			httpQuery += String.format("query.fields[%d].value=%s&", i,
					qf.getValue());
			i++;

		}
		char[] chars = httpQuery.toCharArray();
		char[] nchars = Arrays.copyOfRange(chars, 0, chars.length - 1);
		return new String(nchars);
	}

	public int getPage() {
		return page;
	}

	public int getRows() {
		return rows;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public void setRows(int rows) {
		this.rows = rows;
	}

	public void reset() {
		prefix = "a.";
		fields = Lists.newArrayList();
		orders = Lists.newArrayList();
		groupBy = new GroupBy();		
		sqlId = "";
	}
	
	
	@Override
	public RunnerQuery clone() {
		try {
			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(bout);
			out.writeObject(this);
			out.close();

			ByteArrayInputStream bin = new ByteArrayInputStream(
					bout.toByteArray());
			ObjectInputStream in = new ObjectInputStream(bin);
			Object ret = in.readObject();
			return (RunnerQuery) ret;

		} catch (Exception e) {
			return null;
		}
	}
}
